﻿using Newtonsoft.Json;
using Quartz;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using T.FAS.Runtime.Base.Common;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.ScheduleJob.SPI;
using T.ServiceComponent.Error;
using T.ServiceComponent.Kernel.Service;
using T.ServiceComponent.Monitor.Client;
using ExceptionHelper = T.FAS.Runtime.Base.Common.ExceptionHelper;

namespace T.FAS.Runtime.ScheduleJob.Engine
{
    public class ScheduleJobEngineMonitor
    {
        #region 监控元数据
        private static string MD_RTPEngineInvokeTotalTPS = "FAS.ScheduleJob.RTPEngineInvokeTotal.TPS";
        private static string MD_RTPEngineInvokeSuccessTPS = "FAS.ScheduleJob.RTPEngineInvokeSuccess.TPS";
        private static string MD_RTPEngineInvokeFailTPS = "FAS.ScheduleJob.RTPEngineInvokeFail.TPS";
        private static string MD_RTPEngineInvokeSuccessDuration = "FAS.ScheduleJob.RTPEngineInvokeSuccess.Duration";

        #endregion
        /// <summary>
        /// 全链路
        /// </summary>
        /// <param name="opera"></param>
        /// <param name="start"></param>
        /// <param name="jobContext"></param>
        /// <param name="e"></param>
        public static void Trace(string opera, DateTime start, ScheduleJobExecContext jobContext, Exception e)
        {
            if (jobContext == null) return;
            try
            {
                var endTime = DateTime.Now;
                var span = endTime - start;
                var rpcID = RpcContext.Current.RpcID;
                var requestID = RpcContext.Current.RequestID;

                var longValues = new Dictionary<string, long>() { };
                var doubleValues = new Dictionary<string, double>() { { "TimeSpan", span.TotalMilliseconds } };

                var tag = new Dictionary<string, string>()
                {
                    { "ScheduleJobID", jobContext.ScheduleJobMetaData.ID},
                    { "SCheduleJobCode",jobContext.ScheduleJobMetaData.Code },
                };

                var context = new Dictionary<string, string>()
                {
                    { "Url", "FAS-ScheduleJob"},
                    { "TraceType", "FAS-ScheduleJob"},
                    { "Opera",opera},
                    { "RequestID", requestID },
                    { "RpcID", rpcID },
                    { "HostName", FASContextHelper.GetInstance().HostName },
                    { "ProcessName", FASContextHelper.GetInstance().ProcessName },
                    { "ScheduleJobID", jobContext.ScheduleJobMetaData.ID},
                    { "InstanceID", jobContext.Instance.ID},
                    { "ScheduleJobCode",jobContext.ScheduleJobMetaData.Code },
                    { "ScheduleJobType", Convert.ToString(jobContext.ScheduleJobMetaData.JobType)},
                    { "ScheduleJobContent",jobContext.ScheduleJobMetaData.JobContent},
                    { "ScheduleJobTrigger",JsonConvert.SerializeObject(jobContext.ScheduleJobMetaData.ScheduleTrigger)},
                    { "BeginTime", start.ToString("yyyy-MM-dd HH:mm:ss.fff")},
                    { "EndTime", endTime.ToString("yyyy-MM-dd HH:mm:ss.fff")},
                };


                if (e != null)
                {
                    context.Add("InvokeResult", "Error");
                    context.Add("ExceptionMessage", e.Message);
                }
                else
                    context.Add("InvokeResult", "OK");

                MonitorClient.Trace(requestID, rpcID, longValues, doubleValues, tag, context);
            }
            catch
            { }
        }
        /// <summary>
        /// 上报根因
        /// </summary>
        /// <param name="opera"></param>
        /// <param name="start"></param>
        /// <param name="jobContext"></param>
        /// <param name="e"></param>
        public static void RootCaseReport(string opera, DateTime start, ScheduleJobExecContext jobContext, Exception e)
        {
            if (e == null)
                return;
            var ex = ExceptionHelper.GetLastError(e);
            if (ex == null)
                return;
            //如果是远程异常，不上报异常根因监控
            if (ExceptionHelper.IsRemoteException(e))
                return;
            if (jobContext == null)
                return;
            var requestID = RpcContext.Current.RequestID;
            var rpcID = RpcContext.Current.RpcID;
            var endTime = DateTime.Now;
            Dictionary<string, string> tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, string> context = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            try
            {
                //Tag：程序信息
                tags.Add("HostID", FASContextHelper.GetInstance().HostID);         //异常发生地
                tags.Add("HostType", FASContextHelper.GetInstance().HostType);     //异常发生地
                tags.Add("ClientIP", FASContextHelper.GetInstance().HostName);  //异常发生地
                tags.Add("ServerIP", ExceptionHelper.GetTargetFromContext(e)); //程序访问的远程目标地址

                //Tag：服务信息
                tags.Add("SourceServiceID", opera);   //发生异常时所在的服务ID
                tags.Add("TargetServiceID", jobContext.ScheduleJobMetaData.Code);   //程序访问的目标服务ID
                tags.Add("ScheduleJobID", jobContext.ScheduleJobMetaData.ID);
                tags.Add("SCheduleJobCode", jobContext.ScheduleJobMetaData.Code);

                //Tag：异常信息
                tags.Add("ErrorType", Convert.ToString(ex.GetType()));
                tags.Add("ErrorMessage", ex.Message);
                tags.Add("ErrorCode", (ex is TException) == true ? (ex as TException).Code : "");
                tags.Add("ErrorSource", ex.Source);
                tags.Add("ErrorLevel", ExceptionHelper.GetErrorLevel(ex));

                //Context：全链路信息
                context.Add("RequestID", requestID);
                context.Add("RpcID",rpcID);
                context.Add("TraceType", "ScheduleJob");
                context.Add("ScheduleJobType", Convert.ToString(jobContext.ScheduleJobMetaData.JobType));
                context.Add("ScheduleJobContent", jobContext.ScheduleJobMetaData.JobContent);
                context.Add("ScheduleJobTrigger", JsonConvert.SerializeObject(jobContext.ScheduleJobMetaData.ScheduleTrigger));
                context.Add("BeginTime", start.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                context.Add("EndTime", endTime.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                context.Add("ProcessName", FASContextHelper.GetInstance().ProcessName);

                MonitorClient.Send(FASContextHelper.GetInstance().RootCaseMonitorMetaData, 1, tags, context);
            }
            catch
            { }
        }
        public static void MointorRTPEngineInvokeTotalTPS(ScheduleJobExecContext jobContext)
        {
            try
            {
                MonitorClient.Send(MD_RTPEngineInvokeTotalTPS, 1,
                    new Dictionary<string, string>()
                    {
                        { "ScheduleJobCode",jobContext.ScheduleJobMetaData.Code},
                        { "ScheduleJobType",Convert.ToString(jobContext.ScheduleJobMetaData.JobType)}
                    });
            }
            catch
            { }
        }
        public static void MointorRTPEngineInvokeSuccessTPS(ScheduleJobExecContext jobContext)
        {
            try
            {
                MonitorClient.Send(MD_RTPEngineInvokeSuccessTPS, 1,
                    new Dictionary<string, string>()
                    {
                        { "ScheduleJobCode",jobContext.ScheduleJobMetaData.Code},
                        { "ScheduleJobType",Convert.ToString(jobContext.ScheduleJobMetaData.JobType)}
                    });
            }
            catch
            { }
        }

        public static void MointorRTPEngineInvokeFailTPS(ScheduleJobExecContext jobContext)
        {
            try
            {
                MonitorClient.Send(MD_RTPEngineInvokeFailTPS, 1,
                    new Dictionary<string, string>()
                    {
                        { "ScheduleJobCode",jobContext.ScheduleJobMetaData.Code},
                        { "ScheduleJobType",Convert.ToString(jobContext.ScheduleJobMetaData.JobType)}
                    });
            }
            catch
            { }
        }

        public static void MointorRTPEngineInvokeSuccessDuration(ScheduleJobExecContext jobContext,double duration)
        {
            try
            {
                MonitorClient.Send(MD_RTPEngineInvokeSuccessDuration, duration,
                    new Dictionary<string, string>()
                    {
                        { "ScheduleJobCode",jobContext.ScheduleJobMetaData.Code},
                        { "ScheduleJobType",Convert.ToString(jobContext.ScheduleJobMetaData.JobType)},
                        { "HostNameProcess",FASContextHelper.GetInstance().HostName+FASContextHelper.GetInstance().ProcessID}
                    });
            }
            catch
            { }
        }
    }
}
